package org.example.uitls;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Java SE 正则表达式 API Pattern 与 Matcher
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2023/1/11 17:03
 */
public class PatternTest {

    /**
     * static Pattern compile(String regex)	将给定的正则表达式编译为模式
     * Matcher matcher(CharSequence input)	创建一个匹配器，匹配给定的输入与此模式是否匹配。
     */
    @Test
    public void matcherTest1() {
        // [0-9]*：匹配零个或多个数字
        Pattern pattern0_9 = Pattern.compile("[0-9]*");
        Matcher matcher1 = pattern0_9.matcher("314");
        Matcher matcher2 = pattern0_9.matcher("3.14");
        // boolean matches() 	尝试将整个区域与模式匹配。即必须全部匹配才返回 true.
        // true
        System.out.println(matcher1.matches());
        // false
        System.out.println(matcher2.matches());
    }

    /**
     * static boolean matches(String regex, CharSequence input)
     * 编译给定的正则表达式，并尝试匹配给定的输入是否全部匹配。
     */
    @Test
    public void matchesTest1() {
        // "\\w+"：匹配大小写字母、数字、下划线，"\w" 表示单词字符，即 大小写字母、数字、下划线。
        // matches 方法将整个整个字符串与模式进行匹配，匹配成功返回 true，否则 false
        // 第一个反斜杆是转义字符
        // true
        System.out.println(Pattern.matches("\\w+", "Wmx_328"));
        // false
        System.out.println(Pattern.matches("\\w+", "Wmx3.28"));
    }

    /**
     * String[] split(CharSequence input)
     * 将给定的输入序列用正则模式进行匹配分割，未匹配上时，返回整个目标字符串
     * String[] split(CharSequence input, int limit)
     * 限制匹配次数为 limit -1 次，负数和0表示不做限制.
     */
    @Test
    public void splitTest1() {
        // 匹配下划线
        Pattern pattern_ = Pattern.compile("_");
        String input = "89U_78P_G67-8P_长城";
        // 使用正则模式对目标字符串进行分割，limit=0 等同于 split(input)
        // limit=2实质是分割一次，匹配一次后不再进行匹配分割
        String[] split1 = pattern_.split(input);
        String[] split2 = pattern_.split(input, 0);
        String[] split3 = pattern_.split(input, 2);
        // [89U, 78P, G67-8P, 长城]
        System.out.println(Arrays.asList(split1));
        // [89U, 78P, G67-8P, 长城]
        System.out.println(Arrays.asList(split2));
        // [89U, 78P_G67-8P_长城]
        System.out.println(Arrays.asList(split3));
    }

    /**
     * boolean matches() 	尝试将整个区域与模式匹配，即必须全部匹配才返回 true.
     * boolean lookingAt() 	尝试将从区域开头开始的输入序列与该模式匹配，即从头开始匹配，只要头部一部分匹配上就行，不需要整个匹配.
     * * 注意此方法执行之后，匹配器位置已经发生移动。
     * boolean find() 	尝试查找与该模式匹配的输入序列的下一个子序列。即只要其中一部分能匹配上，就返回 true。
     * * 注意此方法执行之后，匹配器位置已经发生移动。
     * boolean find(int start）	重置此匹配器，然后尝试查找匹配该模式、从指定索引开始的输入序列的下一个子序列。
     * * 注意此方法执行之后，匹配器位置已经发生移动。
     */
    @Test
    public void lookingAtTest1() {
        // 匹配小写字母一个或者多个
        Pattern pattern_a_z = Pattern.compile("[a-z]+");
        // Matcher matcher(CharSequence input)	创建一个匹配器，匹配给定的输入与此模式是否匹配.
        Matcher matcher1 = pattern_a_z.matcher("<span>123</span>");
        Matcher matcher2 = pattern_a_z.matcher("span123");
        Matcher matcher3 = pattern_a_z.matcher("span");
        // true,false,false
        System.out.println(matcher1.find() + "," + matcher1.matches() + "," + matcher1.lookingAt());
        // true,false,true
        System.out.println(matcher2.find() + "," + matcher2.matches() + "," + matcher2.lookingAt());
        // true,true,true
        System.out.println(matcher3.find() + "," + matcher3.matches() + "," + matcher3.lookingAt());
    }

    /**
     * boolean find() 	尝试查找与该模式匹配的输入序列的下一个子序列。即只要其中一部分能匹配上，就返回 true。
     * * 注意此方法执行之后，匹配器位置已经发生移动。
     * boolean find(int start）	重置此匹配器，然后尝试查找匹配该模式、从指定索引开始的输入序列的下一个子序列。
     * * 注意此方法执行之后，匹配器位置已经发生移动。
     * int start() 	返回上一个匹配的起始索引
     * int start(int group)	返回在上一个匹配操作期间，由给定组所捕获的子序列的初始索引
     * int end()	返回最后一个字符匹配后的偏移量。
     * int end(int group)	返回在上一个匹配操作期间，由给定组所捕获子序列的最后字符之后的偏移量。
     */
    @Test
    public void findTest1() {
        Pattern pattern_a_z = Pattern.compile("[a-z]+");
        Matcher matcher = pattern_a_z.matcher("2020 i love you! 0329");
        while (matcher.find()) {
            // start=5,end=6
            // start=7,end=11
            // start=12,end=15
            System.out.println("start=" + matcher.start() + ",end=" + matcher.end());
        }
    }

    /**
     * Matcher appendReplacement(StringBuffer sb, String replacement)	实现非终端添加和替换步骤。
     * StringBuffer appendTail(StringBuffer sb)	实现终端添加和替换步骤。
     * String replaceAll(String replacement)	替换匹配到的输入字符串中的每个子序列。
     * String replaceFirst(String replacement)	 替换匹配到的输入字符串中的第一个子序列。
     * static String quoteReplacement(String s)	返回指定字符串的字面替换字符串。这个方法返回一个字符串，就像传递给Matcher类的appendReplacement 方法一个字面字符串一样工作。
     */
    @Test
    public void replaceAllTest1() {
        // [] 表示字符集，匹配其中包含的任一字符
        Pattern pattern_code = Pattern.compile("[<>\\w'/=:]");
        String input = "<span style='color:red'>万里长城</span>";
        Matcher matcher = pattern_code.matcher(input);

        boolean find = matcher.find();
        // 替换匹配到的所有子序列为空，输出：find = true, 万里长城
        String replaceAll = matcher.replaceAll("");
        // find = true, 万里长城
        System.out.println("find = " + find + "," + replaceAll);

        // 重置匹配器，重新开始匹配
        matcher.reset();

        // 注意：虽然 matchers 返回 false，但是对于匹配上的目标照样能进行替换
        // 输出：matches = false, 万里长城
        boolean matches = matcher.matches();
        String replaceAll2 = matcher.replaceAll("");
        // matches = false, 万里长城
        System.out.println("matches = " + matches + "," + replaceAll2);

        // replaceFirst：只替换匹配的第一个，输出：span style='color:red'>万里长城</span>
        System.out.println(matcher.replaceFirst(""));
    }

    /**
     * 提取文本中的时间
     *
     * @param input ；含有日期的文本，如 网易 2020-03-29 10:45:00 来源: 央视新闻客户端
     * @return ：返回提取好的时间，如 20200329104500
     */
    public String parserDate(String input) {
        String result = null;

        // Pattern 类是线程安全的
        // pattern1：提取 yyyy-MM-dd HH:mm:ss
        // pattern2：提取 yyyy-MM-dd HH:mm
        // pattern3：提取 yyyy-MM-dd

        Pattern pattern1 = Pattern.compile("[\\s\\W]*(\\d{4})[-年_\\s](\\d{1,2})[-月_\\s](\\d{1,2})[\\s\\W]*(\\d{1,2})[:：时](\\d{1,2})[:：分](\\d{1,2})[\\s\\W]*");
        Pattern pattern2 = Pattern.compile("[\\s\\W]*(\\d{4})[-年_\\s](\\d{1,2})[-月_\\s](\\d{1,2})[\\s\\W]*(\\d{1,2})[:：时](\\d{1,2})[\\s\\W]*");
        Pattern pattern3 = Pattern.compile("[\\s\\W]*(\\d{4})[-年_\\s](\\d{1,2})[-月_\\s](\\d{1,2})[\\s\\W]*");

        // 无论哪个规则匹配上，都赋值给公共的 matcher，用于后续统一处理
        Matcher matcher1 = pattern1.matcher(input);
        Matcher matcher2 = pattern2.matcher(input);
        Matcher matcher3 = pattern3.matcher(input);
        Matcher matcher = null;
        //注意只有先匹配上正则表达式，才能用 matcher.group 取值.
        if (matcher1.find()) {
            matcher = matcher1;
        } else if (matcher2.find()) {
            matcher = matcher2;
        } else if (matcher3.find()) {
            matcher = matcher3;
        }
        if (matcher != null) {
            //待取值的分组个数，值先用 list 存储
            int groupCount = matcher.groupCount();
            List<String> dataList = new ArrayList<>(6);
            //注意 group 索引从1开始，按顺序匹配正则中的 ()
            for (int i = 0; i < groupCount; i++) {
                String group = matcher.group(i + 1);
                //对于 月、天、时、分、秒 很可能出现单位数，如 2018-8-8 3:2:6，所以需要补齐
                group = group.length() == 1 ? "0" + group : group;
                dataList.add(group);
            }
            //最后对于不满足 yyyyMMddHHmmss 的，比如 2018-8-8，后续统一用 00 代替
            for (int j = dataList.size(); j < 6; j++) {
                dataList.add("00");
            }
            //直接将 list 转字符串 [2020, 03, 29, 10, 45, 00]，然后去掉其中不需要的字符 [、]、, 以及空格
            result = dataList.toString().replaceAll("[\\[\\],\\s]", "");
        }
        return result;
    }

    @Test
    public void parserDateTest() {
        // 20200329104500
        System.out.println(parserDate("网易 2020-03-29 10:45:00 来源: 央视新闻客户端"));
        // 20200309034000
        System.out.println(parserDate("搜狐 2020-03-9 3:40 来源:环球网"));
        // 20200328200823
        System.out.println(parserDate("凤凰 2020年3月28日 20:8:23 "));
        // 20200108000000
        System.out.println(parserDate("腾讯 2020年1月8日  今天天气晴."));
        // null
        System.out.println(parserDate("腾讯 2020年的1月8日号  今天天气晴."));
    }

}
